module axi_spi(
	//------clk and rst------ 
	input rst,
	input clk,
	//------spi interface-----
	input spi_ssel,
	input spi_sck,
	input spi_mosi,
	output spi_miso,
	//------axi4 lite interface-----
	output        arvalid,
	output [31:0] araddr,
	input         arready,
	output        awvalid,
	output [31:0] awaddr,
	input         awready,
	input         rvalid,
	input  [31:0] rdata,
	output [1:0]  rresp,
	output        rready,
	output        wvalid,
	output [31:0] wdata,
	output [3:0]  wstrb,
	input         wready
	);
	
	parameter CMD_WR = 0;
	parameter CMD_RD = 1;
	
	parameter FSM_IDLE = 3'b000;
	parameter FSM_WAIT_SPI_WDATA = 3'b001;
	parameter FSM_AXILITE_WADDR = 3'b010;
	parameter FSM_AXILITE_WDATA = 3'b011;
	parameter FSM_WAIT_SPI_RADDR = 3'b100;
	parameter FSM_AXILITE_RADDR = 3'b101;
	parameter FSM_AXILITE_RDATA = 3'b110;
	parameter FSM_WAIT = 3'b111;
	
	parameter OFFSET_CMD = 0;
	parameter OFFSET_ADDR0 = 1;
	parameter OFFSET_ADDR1 = 2;
	parameter OFFSET_ADDR2 = 3;
	parameter OFFSET_ADDR3 = 4;
	parameter OFFSET_WMASK = 5;
	parameter OFFSET_DATA0 = 6;
	parameter OFFSET_DATA1 = 7;
	parameter OFFSET_DATA2 = 8;
	parameter OFFSET_DATA3 = 9;

	
	
	wire       di_req;
	reg  [7:0] di;
	reg        wren;
	wire       wr_ack;
	wire       do_valid;
	wire [7:0] do;
	
	reg [1:0]  cmd_reg;
	reg [31:0] addr_reg;
	reg [3:0]  mask_reg;
	reg [31:0] wdata_reg;
	reg [31:0] rdata_reg;
	
	reg        spi_ssel_1d;
	reg        spi_ssel_2d;
	reg        do_valid_1d;
	reg        di_req_1d;
	wire       do_pulse;
	wire       di_pulse;
	
	reg  [2:0] fsm;
	
	reg  [3:0] do_cnt;
	reg  [3:0] di_cnt;
	
	
	    
spi_slave #(.N(8)) u_spi_slave (
	.clk_i      (clk       ),
	.spi_ssel_i (spi_ssel  ),
	.spi_sck_i  (spi_sck   ),
	.spi_mosi_i (spi_mosi  ),
	.spi_miso_o (spi_miso  ),
	.di_req_o   (di_req    ),
	.di_i       (di        ),
	.wren_i     (wren      ),
	.wr_ack_o   (wr_ack    ),
	.do_valid_o (do_valid  ),
	.do_o       (do        )
	);
	

always @(posedge rst,posedge clk) begin
	if(rst==1'b1) begin
		spi_ssel_1d<=1'b0;
		spi_ssel_2d<=1'b0;
		do_valid_1d<=1'b0;
		di_req_1d<=1'b0;
	end else begin
		spi_ssel_1d<=spi_ssel;
		spi_ssel_2d<=spi_ssel_1d;
		do_valid_1d<=do_valid;
		di_req_1d<=di_req;
	end
end

assign do_pulse = (do_valid_1d==1'b0 && do_valid==1'b1) ? 1'b1 : 1'b0;
assign di_pulse = (di_req_1d==1'b0 && di_req==1'b1) ? 1'b1 : 1'b0;

always @(posedge rst,posedge clk) begin
	if(rst==1'b1) begin
		do_cnt<=0;
		addr_reg<=0;
		mask_reg<=0;
		wdata_reg<=0;
	end else if (spi_ssel_2d==1'b1)begin
		do_cnt<=0;
		addr_reg<=0;
		mask_reg<=0;
		wdata_reg<=0;
	end else if (do_pulse==1'b1) begin
		do_cnt<=do_cnt+1'b1;
		
		if (do_cnt==OFFSET_CMD) begin
			cmd_reg<=do[1:0];
		end else if (do_cnt==OFFSET_ADDR0) begin
			addr_reg[31:24]<=do;
		end else if (do_cnt==OFFSET_ADDR1) begin
			addr_reg[23:16]<=do;
		end else if (do_cnt==OFFSET_ADDR2) begin
			addr_reg[15:8]<=do;
		end else if (do_cnt==OFFSET_ADDR3) begin
			addr_reg[7:0]<=do;
		end else if (do_cnt==OFFSET_WMASK) begin
			mask_reg<=do;
		end else if (do_cnt==OFFSET_DATA0) begin
			wdata_reg[31:24]<=do;
		end else if (do_cnt==OFFSET_DATA1) begin
			wdata_reg[23:16]<=do;
		end else if (do_cnt==OFFSET_DATA2) begin
			wdata_reg[15:8]<=do;
		end else if (do_cnt==OFFSET_DATA3) begin
			wdata_reg[7:0]<=do;
		end
	end
end

always @(posedge rst,posedge clk) begin
	if(rst==1'b1) begin
		di_cnt<=0;
		di<=0;
		wren<=0;
	end else if (spi_ssel_2d==1'b1)begin
		di_cnt<=0;
		di<=0;
	end else if (wr_ack==1'b1) begin
		wren<=1'b0;
	end else if (di_pulse==1'b1) begin
		di_cnt<=di_cnt+1'b1;
		wren<=1'b1;
		
		if (cmd_reg==CMD_RD) begin
			if (di_cnt==OFFSET_DATA0-1) begin
				di<=rdata_reg[31:24];
			end else if (di_cnt==OFFSET_DATA1-1) begin
				di<=rdata_reg[23:16];
			end else if (di_cnt==OFFSET_DATA2-1) begin
				di<=rdata_reg[15:8] ;
			end else if (di_cnt==OFFSET_DATA3-1) begin
				di<=rdata_reg[7:0];
			end else begin
				di<=0;
			end
		end
	end
end

always @(posedge rst,posedge clk) begin
	if(rst==1'b1) begin
		rdata_reg<=0;
	end else if (rvalid==1'b1 && rready==1'b1) begin
		rdata_reg<=rdata;
	end
end



always @(posedge rst,posedge clk) begin
	if (rst==1'b1) begin
		fsm<=FSM_IDLE;
	end else if (spi_ssel_2d==1'b1) begin
		fsm<=FSM_IDLE;
	end else begin
		case (fsm)
		FSM_IDLE:
		begin
			if (do_pulse==1'b1 && do_cnt==OFFSET_CMD ) begin
				if (do[1:0]==CMD_WR) begin
					fsm<=FSM_WAIT_SPI_WDATA;
				end else if (do[1:0]==CMD_RD) begin
					fsm<=FSM_WAIT_SPI_RADDR;
				end
			end
		end
		FSM_WAIT_SPI_WDATA:
		begin
			if (do_pulse==1'b1 && do_cnt>=OFFSET_DATA3 )	begin
				fsm<=FSM_AXILITE_WADDR;
			end
		end
		FSM_AXILITE_WADDR:
		begin
			if(awvalid==1'b1 && awready==1'b1) begin
				fsm<=FSM_AXILITE_WDATA;
			end
		end
		FSM_AXILITE_WDATA:
		begin
			if(wvalid==1'b1 && wready==1'b1) begin
				fsm<=FSM_WAIT;
			end
		end
		FSM_WAIT_SPI_RADDR:
		begin
			if (do_pulse==1'b1 && do_cnt>=OFFSET_ADDR3 )	begin
				fsm<=FSM_AXILITE_RADDR;
			end
		end
		FSM_AXILITE_RADDR:
		begin
			if(arvalid==1'b1 && arready==1'b1) begin
				fsm<=FSM_AXILITE_RDATA;
			end
		end
		FSM_AXILITE_RDATA:
		begin
			if (rvalid==1'b1 && rready==1'b1) begin
				fsm<=FSM_WAIT;
			end
		end
		FSM_WAIT:
			fsm<=FSM_WAIT;
		default:
		begin
			fsm<=FSM_IDLE;
		end
		endcase
	end
end
			

	
	
assign awvalid = (fsm==FSM_AXILITE_WADDR) ? 1'b1 : 1'b0;
assign awaddr  = addr_reg;

assign wvalid  = (fsm==FSM_AXILITE_WDATA) ? 1'b1 : 1'b0;
assign wdata   = wdata_reg;
assign wstrb   = mask_reg[3:0];

assign arvalid = (fsm==FSM_AXILITE_RADDR) ? 1'b1 : 1'b0;
assign araddr  = addr_reg;

assign rresp   = 2'b00;
assign rready  = (fsm==FSM_AXILITE_RDATA) ? 1'b1 : 1'b0;



endmodule
